package scala.examples.fs
import scala.util.parsing.combinator._
import scala.util.parsing.combinator.lexical._
import scala.util.parsing.combinator.syntactical._
import scala.Predef.{stringWrapper => _, _} // disable the stringWrapper implicit conversion

/** Featherweight Scala parser
 *
 * Concrete syntax: <pre>
 * name = ident
 * label = ident
 * path = bound(name) opt('.' repsep(name, '.'))
 *
 * term = 'new' type 
 *      | 'val' bind(name, x) ':' type '=' term 'in' in(term, x) 
 *      | 'unit'
 *      | path '(' seprep(term, ',') ')' 
 *      | path  
 *
 * sig = '(' repsep(type, ',') ')' '{' bind(name, self) '|' in(rep(member), self) '}' 
 * type = sig | 'Unit' | path '.' 'type' | path   
 *
 * member = 'val'   label ':' type opt('=' term)
 *        | 'def'   label '(' repsep(bind(name, as) ':' type, ',') ')' ':' type opt('=' in(term, as))?
 *        | 'type'  label opt('=' type)
 *        | 'trait' label sig   
 *</pre>
 */  
trait Parser extends StdTokenParsers with BindingParsers with ImplicitConversions with SyntaxIsMappable {   
  type Tokens = StdLexical 
  val lexical = new StdLexical
  import lexical.{token, Identifier}
  
  lexical.reserved ++= List("new", "val", "in", "unit", "Unit", "type", "def", "trait")
  lexical.delimiters ++= List('.', '(', ')', '{', '}', '=', ':', ',')

  // name = ident
  def name :Parser[Name] = accept("name", {case Identifier(n) => Name(n)})
  
  // label = ident
  def label: Parser[String] = accept("label", {case Identifier(n) => n})
  
  // path = bound(name) opt('.' repsep(name, '.'))
  def path :Parser[Path] = bound(name) ~ (("." ~ name*("."))?) ^^ headOptionTailToFunList(Path)
  
  /** left-recursive parser for terms: <pre> 
   *  term = 'new' type 
   *    | 'val' bind(name, x) ':' type '=' term 'in' in(term, x) 
   *    | 'unit'
   *    | '(' term ')'
   *    | term '.' label '(' seprep(term, ',') ')' 
   *    | term '.' label 
   *  </pre>
   */
   /** right-recursive rewrite: <pre>
    *  target = 'new' type 
    *    | 'val' bind(name, x) ':' type '=' term 'in' in(term, x) 
    *    | 'unit'
    *    | name
    *    | '(' term ')'
    *   
    *  actuals = '(' seprep(term, ',') ')'
    *
    *  term = 
    *      target rep('.' name opt(actuals))
    */  
  def target: Parser[Term] =  
     ( "new"  ~! typeP                                                         ^^ New 
     | newScope[Name] >> { b => 
       "val" ~! bind(name, b) ~ ":" ~ typeP ~ "=" ~ term ~ "in" ~ in(term, b)} ^^ flatten3(Let) 
     | "unit"                                                                  ^^ TMUnit
     | bound(name)                                                             ^^ Path_.fromName
     | "(" ~ term ~ ")"
     )

   def actuals: Parser[List[Term]] = "(" ~ term*(",") ~ ")"

   // a term starts with a target, followed by any number of (field selection or method call)'s, separated by "."
   def term: Parser[Term] =   {
      /** Combine a term and a method call or field selection into a new term
       *
       * @pre tgt.isPath implies tgt.isInstanceOf[Path]
       * @post @result.isPath implies @result.isInstanceOf[Path]
       */
      def combine(tgt: Term, sel: ~[Name, Option[List[Term]]]) = sel match {
        case (name@Name(n)) ~ Some(args) => MethodCall(tgt, n, args)
        case (name@Name(n)) ~ None => tgt match {
          case p: Path => p ++ Path_.fromName(name) 
          case _ => FieldSel(tgt, n)
        }
      }
      
      chainl1(target, name ~ opt(actuals), "." ^^ &combine)
   }
   
  /** <pre>
   *  type = sig | 'Unit' | path '.' 'type' | path   
   *  </pre>   
   */
  def typeP: Parser[Type] = 
     ( sig 
     | "Unit"              ^^ TPUnit
     | path ~ "." ~ "type" ^^ SingletonType
     | path                ^? {case target: Path if target.comps.length > 1 => target.split match {case (qual, name) => TypeSel(qual, name) }}
     // there are no user-defined top-level classes, hence the condition target.comps.length > 1
     )                           
   
  /** <pre>
   *  sig = '(' repsep(type, ',') ')' '{' bind(name, self) '=>' in(rep(member), self) '}'
   *  </pre>
   */
  def sig: Parser[TypeSig] = newScope[Name] >> { self =>
       "(" ~ (typeP*(",")) ~ ")" ~ "{" ~ bind(name, self) ~ "=>" ~ in(member*, self) ~ "}"} ^^ flatten2(TypeSig)
    
  
  /** <pre>
   *  member = 
   *      'val'   label ':' type opt('=' term)
   *    | 'type'  label opt('=' type)
   *    | 'trait' label sig   
   *    | 'def'   label '(' repsep(bind(name, as) ':' type, ',') ')' ':' type opt('=' in(term, as))?
   *  </pre>
   */
  def member: Parser[Member] = 
    ( "val"   ~! label ~ ":" ~ typeP ~ opt("=" ~! term) ^^ flatten3(MField)
    | "type"  ~! label ~ opt("=" ~! typeP)              ^^ flatten2(MType)
    | "trait" ~! label ~ sig                            ^^ flatten2(MTrait)
    | "def"   ~ newScope[Name] >> {as => 
                 label ~ "(" ~ ((bind(name, as) ~ ":" ~! typeP)*(",")) ~ ")" ~ ":" ~ typeP ~ 
                          opt("=" ~! in(term, as))}     ^^ flatten4(MMethod)   
    )
  
}



 
 /* def term = target ~ rep("." ~ name ~ opt(actuals))        ^^ flatten2(mkTerm)
 def mkTerm(tgt: Term, arg_sels: List[{Name, Option[List[Term]]}]): Term = {
    // @pre tgt.isPath implies tgt.isInstanceOf[Path]
    def buildSelection(tgt: Term, arg_sel: {Name, Option[List[Term]]}): Term = arg_sel match {
      case {name@Name(n), None} => tgt match {case p: Path => p ~ Path_.fromName(name) case _ => FieldSel(tgt, n)}
      case {name@Name(n), Some(args)} => MethodCall(tgt, n, args) 
    }
      
    arg_sels.foldLeft(if(tgt.isPath) Path_.fromTerm(tgt) else tgt)(buildSelection)
  }*/
    
  /*def debug(msg: String) = new UnitParser {
   def apply(in: Input) = {Console.println(msg); Success((), in)}
 }*/
   
   